package bj.gui

import scala.swing.event
import event.ButtonClicked
import scala.swing.SimpleGUIApplication
import scala.swing.Label
import scala.swing.Swing
import scala.swing.Button
import scala.swing.FlowPanel
import scala.swing.BorderPanel
import scala.swing.BoxPanel
import scala.swing.Orientation
import scala.swing.MainFrame
import scala.swing.Table
import scala.swing.MenuBar
import scala.swing.Menu
import scala.swing.MenuItem
import scala.swing.Action
import scala.swing.event.TableRowsSelected
import scala.swing.ScrollPane
import java.awt.Dimension
import javax.swing.ImageIcon
import scala.swing.Frame
import scala.actors.Actor
import scala.actors.Actor._
import scala.swing.event.Event
import bj.actor.House
import bj.actor.SendMsg
import bj.actor.GetTables
import bj.actor.SendTables
import bj.actor.TestMessage
import bj.actor.JoinHuman
import bj.actor.JoinedTable
import bj.actor.SendLogin
import bj.actor.SendLogout
import bj.actor.LoggedIn
import bj.actor.AddToBankroll
import bj.actor.Send
import bj.actor.Receive
import bj.actor.Intermediator
import javax.swing.table.DefaultTableModel
import bj.actor.HumanPlayer
import bj.actor.Player
import Actor._
import scala.actors.remote.RemoteActor.{ alive, register }
import scala.actors.remote.RemoteActor
import scala.actors.remote.Node
import scala.actors.AbstractActor
import bj.Config
import org.apache.log4j.PropertyConfigurator



// Lobby behaves like Pong: connects to House, issues request and expects response
// House behaves like Ping: receives a message, processes it and bounces the response 
// back to whoever sent it

object Lobby extends SimpleGUIApplication {
  
  javax.swing.UIManager.setLookAndFeel(Constants.LF)
  PropertyConfigurator.configure("log4j.properties")
  
   // Must specify a class loader:  the ClassLoader that Scala picks when 
  // serializing/deserializing actors is the one Located on RemoteActor which defaults to null.
  // http://stackoverflow.com/questions/3542360/why-is-setting-the-classloader-necessary-with-scala-remoteactors
  RemoteActor.classLoader = getClass.getClassLoader
      
  var myPID : Int = -1    
      
  def top = mainFrame
  var me = new HumanPlayer("",0.0)
  val mainFrame = new MainFrame {
    title = "Blackjack - Lobby View"
    minimumSize = Constants.lobbySize
    menuBar = new MenuBar {
    	contents += new Menu("File") {
    		contents += new MenuItem(Action("Log In") {
    			new Login()
    		})
    		contents += new MenuItem(Action("Log Out") {
    		  logOut
    		})
        contents += new MenuItem(Action("Exit") {
          logOut
          System.exit(0);
        })
 	      }
       contents += new Menu("Help") {
        contents += new MenuItem(Action("Strategy") {
          new BjInfoBox(new Label { icon = new ImageIcon("images/strategy.gif") })
        })
        contents += new MenuItem(Action("Help") {
          new BjInfoBox(new Label("Coming soon!"))
        })
        contents += new MenuItem(Action("About") {
          new BjInfoBox(new Label("<html>Alpha version<br>Copyright 2011<br>Scott Sigalas<br>Anna Clayton</html>"))
        })
       }
 	    }
    
    val lobbyLabel = new Label {
      text = "Lobby"
    }
    var welcomeName = "guest"
    val welcomeLabel = new Label {
      text = "Welcome, " + welcomeName + "!"
    }
    var bankrollValue = 0.0
    val bankrollLabel = new Label {
      text = "Bankroll: $ "+ String.format("%3.2f", double2Double(bankrollValue))
    }
 
    val joinButton = new Button { text = "Join" 
      							  enabled = false}
    val ATMButton = new Button { text = "ATM" 
                                  enabled = false}
    val refreshButton = new Button { text = "Refresh" }
    val invisibleButton = new Button { visible = false }
    
     
     var tableData : Array[Array[AnyRef]] = new Array(0);

      val headers = Array("Table #", "Minimum Bet", "Maximum Bet", "Players", "Dealer")
    
    val tableModel = new DefaultTableModel(
     tableData.asInstanceOf[Array[Array[Object]]], // Data
     headers.asInstanceOf[Array[Object]]) // Column names
    val tablesTable = new Table(tableData.size, headers.size) {model = tableModel
      showGrid = true
      gridColor = Constants.gridColor
      border = Swing.EmptyBorder(20)
      visible = true}
      

    val playerPanel = new BoxPanel(Orientation.Vertical) {
      contents += welcomeLabel
      contents += bankrollLabel
      border = Swing.EmptyBorder(10)
      background = Constants.greenBack
    }
    val topPanel = new BorderPanel() {
      add(new Label { icon = new ImageIcon("images/BJLogo2.gif") }, BorderPanel.Position.West)
      add(lobbyLabel, BorderPanel.Position.Center) 
      add(playerPanel, BorderPanel.Position.East) 
      background = Constants.greenBack
    }
    val midPanel = new FlowPanel() {
      contents += new ScrollPane { viewportView = tablesTable
                                   preferredSize = new Dimension(600,110)}
      background = Constants.greenBack
    }
    
    val buttonPanel = new FlowPanel() {
      contents += joinButton
      contents += ATMButton
      contents += refreshButton
      background = Constants.greenBack
    }
    contents = new BorderPanel() {
      
      add(topPanel, BorderPanel.Position.North) 
      add(midPanel, BorderPanel.Position.Center) 
      add(buttonPanel, BorderPanel.Position.South) 
      border = Swing.EmptyBorder(10)
      background = Constants.greenBack
    }
    
    
    // Listen to the house so I can process events received from it
    implicit val intermediator = Intermediator(RemoteActor.select(Node(Config.IPAddress, Config.PortNumber), Config.HouseServiceName))
    
    listenTo(intermediator)
    listenTo(joinButton)
    listenTo(ATMButton)
    listenTo(refreshButton)
    listenTo(invisibleButton)
    listenTo(tablesTable.selection)
    reactions += {
        // The message from the Lobby to the House have to come from within the GUI because
    	// that is where the Intermediator is listening for them and where the reactions
    	// code will process them. Sending a "click" to an invisible button that's 
    	// listening triggers this. 
      case ButtonClicked(`invisibleButton`) =>
        println("Invisible button click received")
        Send(GetTables)
        println("Should've got tables")
      case ButtonClicked(`joinButton`) => 
        println("Join button clicked")
       if (tablesTable.selection.rows.size > 1)
      new BjInfoBox(new Label("You may play at just one table"))
         //send JoinHuman request to the house to assign the current player to the indicated table
       else
        tablesTable.selection.rows.foreach(x => Send(JoinHuman(myPID,tableModel.getValueAt(x, 0).toString().toInt)))
      case ButtonClicked(`ATMButton`) => 
        println("ATM button clicked")
        new ATM()
      case ButtonClicked(`refreshButton`) =>  
        println("Refresh button clicked")
        tablesTable.selection.rows.clear
        joinButton.enabled = false
        Send(GetTables)
      case TableRowsSelected(`tablesTable`, range, adjusting) =>     
         if (welcomeName != "guest")
        	 joinButton.enabled = true
       
      // Receive an informational message from the house. Display an informational message with the text
      case Receive(SendMsg(value)) => new BjInfoBox(new Label { text = value })  
        // An updated array of table data has been received from the house.  
      // Remove all current rows from the tables table and add new rows based on the data received
      case Receive(SendTables(value)) => 
      	println("Refreshing lobby table data")
        tableData.foreach(t => tableModel.removeRow(0))
        tableData = value
    	tableData.foreach(t => tableModel.addRow(t))  
     // Receive a message from the HumanPlayer instance representing this player that a table has been joined. 
      case Receive(JoinedTable(remoteActor)) =>
        println("Got a message from the HumanPlayer that I joined a table")
//        JoinedTable will send a mailbox to the lobby. The lobby can use that remote mailbox to set up a 
//        connection so the PlayerTable GUI can talk to the HumanPlayer object.  
        new PlayerTable(remoteActor)
        // Receive a LoggedIn message from the house supplying the pid of the HumanPlayer that was created for this player
      case Receive(LoggedIn(pid))  => myPID = pid
    }


    
    //log out has been selected from the menu.  Clear indication of the previous login from the GUI and send a SendLogout message to the House
    def logOut {
      joinButton.enabled = false
      ATMButton.enabled = false
      bankrollValue = 0.0
      bankrollLabel.text = "Bankroll: $ "+ String.format("%3.2f", double2Double(bankrollValue))
      welcomeName = "guest"
      welcomeLabel.text = "Welcome " + welcomeName + "!"
      Send(SendLogout(myPID))
    }
    
    // log in has been selected from the menu.  Set the GUI labels to indicate the player's name and bankroll. Send a SendLogIn message to the house 
    def logIn(name : String, bankroll : Double) {
      welcomeName = name
      welcomeLabel.text = "Welcome " + welcomeName + "!"
      bankrollValue = bankroll
      bankrollLabel.text = "Bankroll: $" + String.format("%3.2f", double2Double(bankrollValue))
      ATMButton.enabled = true
      refreshButton.enabled = true
            if (tablesTable.selection.rows.size > 0)
        joinButton.enabled = true
      Send(SendLogin(welcomeName,bankrollValue))   
    }
    
    // the player has used the ATM feature to add to his or her bankroll.  Send a message to the House to increment the player's bankroll
    def addToBankroll(amount : Double) : Unit = {
      Send(AddToBankroll(myPID,amount))
    }
    
    // send a message to the house
    def sendMsg(event : Any) : Unit = {
    println("Lobby is sending "+event.toString)
    Send(event)
  }
    
    // Override the method that's called when the window is closed so we can send the house a logoff message
    override def closeOperation() { 
      this.logOut 
      }
  }

  // The message from the Lobby to the House have to come from within the GUI because
  // that is where the Intermediator is listening for them and where the reactions
  // code will process them. Sending a "click" to an invisible button that's 
  // listening triggers this. See case ButtonClicked for invisibleButton to see
  // how this is done.    
    top.invisibleButton.doClick()
    
    }
